//====================================================================
// File Name : Nand.c
// Function  : S3C2440 8-bit interface Nand Test program(this program use K9k2g16.c).
// Date      : May xx, 2003
// Version   : 0.0
// History
//   R0.0 (200305xx): Modified for 2440 from 2410. -> DonGo
//====================================================================

// 1block=(2048+64)bytes x 64pages

#include <arch.h>
#include <t8.h>
#include <config.h>
#include "nandflash.h"

#define ASM        1
#define C_LANG    2
#define DMA        3
#define TRANS_MODE 2


U32 srcAddress;
U32 targetBlock;        // Block number (0 ~ 4095)
U32 targetSize;        // Total byte size

U32 downloadAddress;
U32 downloadProgramSize=0x0;

//*************************************************
//*************************************************
//**           H/W dependent functions           **
//*************************************************
//*************************************************

//The code is made for bi-endian mode

// block0: reserved for boot strap
// block1~4095: used for OS image
// badblock SE: xx xx xx xx xx 00 ....
// good block SE: ECC0 ECC1 ECC2 FF FF FF ....


#define NF_MECC_UnLock()         {rNFCONT&=~(1<<5);}
#define NF_MECC_Lock()         {rNFCONT|=(1<<5);}
#define NF_SECC_UnLock()         {rNFCONT&=~(1<<6);}
#define NF_SECC_Lock()         {rNFCONT|=(1<<6);}

#define NF_CMD(cmd)            {rNFCMD=cmd;}
#define NF_ADDR(addr)        {rNFADDR=addr;}
#define NF_nFCE_L()            {rNFCONT&=~(1<<1);}
#define NF_nFCE_H()            {rNFCONT|=(1<<1);}
#define NF_RSTECC()            {rNFCONT|=(1<<4);}
#define NF_RDDATA()         (rNFDATA)
#define NF_RDDATA8()         ((*(volatile unsigned char*)0x4E000010) )

#define NF_WRDATA(data)     {rNFDATA=data;}
#define NF_WRDATA8(data)     {rNFDATA8=data;}

#define NF_WAITRB()            {while(!(rNFSTAT&(1<<1)));}
                                //wait tWB and check F_RNB pin.
// RnB Signal
#define NF_CLEAR_RB()            {rNFSTAT |= (1<<2);}    // Have write '1' to clear this bit.
#define NF_DETECT_RB()            {while(!(rNFSTAT&(1<<2)));}

#define ID_K9S1208V0M    0xec76
#define ID_K9K2G16U0M    0xecca

#define    NF16_BAD_OFFSET        (12)

// HCLK=100Mhz
//#define TACLS        0    // 1-clk(0ns)
//#define TWRPH0        6    // 3-clk(25ns)
//#define TWRPH1        0    // 1-clk(10ns)  //TACLS+TWRPH0+TWRPH1>=50ns

#define TACLS        0    // 1-clk(0ns)
#define TWRPH0        6    // 3-clk(25ns)
#define TWRPH1        0    // 1-clk(10ns)  //TACLS+TWRPH0+TWRPH1>=50ns




static U8 se8Buf[16]={
    0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff,
    0xff,0xff,0xff,0xff
};


extern char *hex2char(int val);



void __RdPage512(U8 *bufPt);


int NF_ReadPage(U32 block,U32 page,U8 *buffer)
{
    int i;
    unsigned int blockPage;
    U32 ecc;
    U8 *bufPt=(U8 *)buffer;
    U8 se[16], ecc0, ecc1, ecc2, ecc3;

//        page=page&0x1f;
//        blockPage=(block<<5)+page;
    page = page & (FLASH_PAGESOFBLOCK - 1);
    blockPage = (block << FLASH_PAGESOFBLOCK_SHIFT) + page;
    NF_RSTECC();    // Initialize ECC
    NF_MECC_UnLock();

    NF_nFCE_L();

    NF_CLEAR_RB();
    NF_CMD(0x00);    // Read command
    NF_ADDR(0);     // Column = 0
//    NF_ADDR(blockPage&0xff);        //
    NF_ADDR(0);
//    NF_ADDR((blockPage>>8)&0xff);    // Block & Page num.
//    NF_ADDR((blockPage>>16)&0xff);    //
    NF_ADDR((blockPage)&0xff);    // Block & Page num.
    NF_ADDR((blockPage>>8)&0xff);    //
    NF_ADDR((blockPage>>16)&0xff);

    NF_CMD(0x30);    // Read command
    NF_DETECT_RB();


#if TRANS_MODE==C_LANG
    for (i = 0; i < FLASH_PAGESIZE; i++) {
        *bufPt++=NF_RDDATA8();    // Read one page
    }
#elif TRANS_MODE==DMA
    // Nand to memory dma setting
    rSRCPND=BIT_DMA0;    // Init DMA src pending.
    rDISRC0=NFDATA;     // Nand flash data register
    rDISRCC0=(0<<1) | (1<<0); //arc=AHB,src_addr=fix
    rDIDST0=(unsigned)bufPt;
    rDIDSTC0=(0<<1) | (0<<0); //dst=AHB,dst_addr=inc;
    rDCON0=(1<<31)|(1<<30)|(1<<29)|(1<<28)|(1<<27)|(0<<23)|(1<<22)|(2<<20)|(512/4/4);
    //Handshake,AHB,interrupt,(4-burst),whole,S/W,no_autoreload,word,count=128;

    // DMA on and start.
    rDMASKTRIG0=(1<<1)|(1<<0);

    while(!(rSRCPND & BIT_DMA0));    // Wait until Dma transfer is done.

    rSRCPND=BIT_DMA0;

#elif TRANS_MODE==ASM
    __RdPage512(bufPt);
#endif

    NF_MECC_Lock();
//    NF_SECC_UnLock();


    ecc=rNFMECC0;
    ecc0 = (U8)(ecc &0xff);
    ecc1 = (U8)((ecc>>8) & 0xff);
    ecc2 = (U8)((ecc>>16) & 0xff);
    ecc3 = (U8)((ecc>>24) & 0xff);

    for(i=0;i<16;i++)
    {
        se[i]=NF_RDDATA8();    // Read spare array
     }



/*
rNFMECCD0_0=NF_RDDATA8();
rNFMECCD0_1=NF_RDDATA8();
rNFMECCD0_2=NF_RDDATA8();
rNFMECCD0_3=rNFMECC0_3;
*/
/*
rNFMECCD0=NF_RDDATA();
rNFMECCD0=((rNFMECCD0&0xffffff)|(rNFMECC0&0xff000000));
*/
NF_nFCE_H();

/*
if ((rNFESTAT0&0x3) == 0x0) return OK;
return FAIL;
*/

    if(ecc0==se[0] && ecc1==se[1] && ecc2==se[2] )
    {
        //Uart_Printf("[ECC OK:%x,%x,%x,%x]\n",se[0],se[1],se[2],se[3]);
        return OK;
    }
    else
    {
        //Uart_Printf("[ECC ERROR(RD):read:%x,%x,%x, reg:%x,%x,%x,%x]\n", se[0],se[1],se[2],se[3],ecc0,ecc1,ecc2,ecc3);
        return FAIL;
    }

}

int NF_WritePage(U32 block,U32 page,U8 *buffer)
{
    int i;
    U32 blockPage, Mecc, Secc;
    U8 *bufPt=buffer;

    //NFConDone=0;
       rNFCONT|=(1<<9);
       rNFCONT|=(1<<10);
       //pISR_NFCON= (unsigned)NFCon_Int;
       rSRCPND=BIT_NFCON;
       //rINTMSK=~(BIT_NFCON);//?

    NF_RSTECC();    // Initialize ECC
       NF_MECC_UnLock();
    blockPage=(block<<FLASH_PAGESOFBLOCK_SHIFT)+page;

    NF_nFCE_L();
    NF_CMD(0x0);//??????
    NF_CMD(0x80);   // Write 1st command
    NF_ADDR(0);                // Column 0
    NF_ADDR(0);
    NF_ADDR(blockPage&0xff);        //
    NF_ADDR((blockPage>>8)&0xff);   // Block & page num.
    NF_ADDR((blockPage>>16)&0xff);  //


#if TRANS_MODE==C_LANG

    for(i=0;i<FLASH_PAGESIZE;i++) {
        NF_WRDATA8(*bufPt++);    // Write one page to NFM from buffer
    }
#elif TRANS_MODE==DMA

    // Memory to Nand dma setting
    rSRCPND=BIT_DMA0;    // Init DMA src pending.
    rDISRC0=(unsigned)bufPt;     // Nand flash data register
    rDISRCC0=(0<<1) | (0<<0); //arc=AHB,src_addr=inc
    rDIDST0=NFDATA;
    rDIDSTC0=(0<<1) | (1<<0); //dst=AHB,dst_addr=fix;
    rDCON0=(1<<31)|(1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(1<<22)|(2<<20)|(512/4);
    //  only unit transfer in writing!!!!
    //Handshake,AHB,interrupt,(unit),whole,S/W,no_autoreload,word,count=128;

    // DMA on and start.
    rDMASKTRIG0=(1<<1)|(1<<0);

    while(!(rSRCPND & BIT_DMA0));    // Wait until Dma transfer is done.
    rSRCPND=BIT_DMA0;
#endif
/*
      NF_MECC_Lock();
// Get ECC data.
    // Spare data for 8bit
    // byte  0   1    2    3    4   5
    // ecc  [0]  [1]  [2]  [3]  x   [Bad marking]
    Mecc = rNFMECC0;
    se8Buf[0]=(U8)(Mecc&0xff);
    se8Buf[1]=(U8)((Mecc>>8) & 0xff);
    se8Buf[2]=(U8)((Mecc>>16) & 0xff);
    se8Buf[3]=(U8)((Mecc>>24) & 0xff);
    se8Buf[5]=0xffff;        // Marking good block

    //Write extra data(ECC, bad marking)
    for(i=0;i<16;i++) {
        NF_WRDATA8(se8Buf[i]);    // Write spare array(ECC and Mark)
        NFSpare_Data[i]=se8Buf[i];
    }

     NF_CLEAR_RB();
    NF_CMD(0x10);     // Write 2nd command
    NF_DETECT_RB();

    NF_CMD(0x70);   // Read status command

    for(i=0;i<3;i++);  //twhr=60ns

    if (NF_RDDATA()&0x1) {// Page write error

        Uart_Printf("[PROGRAM_ERROR:block#=%d]\n",block);
        NFMarkBadBlock(block);
         NF_nFCE_H();
        return FAIL;
    } else {
           NF_nFCE_H();
      return OK;

    }
*/

       NF_MECC_Lock();
    // Get ECC data.
    // Spare data for 8bit
    // byte  0     1    2     3     4          5               6      7            8         9
    // ecc  [0]  [1]  [2]  [3]    x   [Bad marking]                    SECC0  SECC1
    Mecc = rNFMECC0;
    se8Buf[0]=(U8)(Mecc&0xff);
    se8Buf[1]=(U8)((Mecc>>8) & 0xff);
    se8Buf[2]=(U8)((Mecc>>16) & 0xff);
    se8Buf[3]=(U8)((Mecc>>24) & 0xff);
    se8Buf[5]=0xff;        // Marking good block

    NF_SECC_UnLock();
    //Write extra data(ECC, bad marking)
    for(i=0;i<4;i++) {
        NF_WRDATA8(se8Buf[i]);    // Write spare array(Main ECC)
        //NFSpare_Data[i]=se8Buf[i];
        }
      NF_SECC_Lock();
    Secc=rNFSECC;
    se8Buf[8]=(U8)(Secc&0xff);
    se8Buf[9]=(U8)((Secc>>8) & 0xff);
    for(i=4;i<16;i++) {
        NF_WRDATA8(se8Buf[i]);  // Write spare array(Spare ECC and Mark)
        //NFSpare_Data[i]=se8Buf[i];
    }
     NF_CLEAR_RB();
    NF_CMD(0x10);     // Write 2nd command
    NF_DETECT_RB();
    //while(NFConDone==0);
     rNFCONT&=~(1<<9);
     rNFCONT&=~(1<<10); // Disable Illegal Access Interrupt
     if(rNFSTAT&0x8) return FAIL;

    NF_CMD(0x70);   // Read status command

    for(i=0;i<3;i++);  //twhr=60ns

       if (NF_RDDATA()&0x1) {// Page write error
               NF_nFCE_H();
        //Uart_Printf("[PROGRAM_ERROR:block#=%d]\n",block);
        //NFMarkBadBlock(block);
        return FAIL;
       } else {
              NF_nFCE_H();
          return OK;
    }

}

int NF_EraseBlock(U32 block)
{
    U32 blockPage=(block<<FLASH_PAGESOFBLOCK_SHIFT);

    //NFConDone=0;
    rNFCONT|=(1<<9);
    rNFCONT|=(1<<10);
    //pISR_NFCON= (unsigned)NFCon_Int;
    rSRCPND=BIT_NFCON;
    //rINTMSK=~(BIT_NFCON);//?

#if BAD_CHECK
    if(NF_IsBadBlock(block))
    return FAIL;
#endif

    NF_nFCE_L();

    NF_CMD(0x60);   // Erase one block 1st command, Block Addr:A9-A25
    // Address 3-cycle
    NF_ADDR(blockPage&0xff);        // Page number=0
    NF_ADDR((blockPage>>8)&0xff);
    NF_ADDR((blockPage>>16)&0xff);


    NF_CLEAR_RB();
    NF_CMD(0xd0);    // Erase one blcok 2nd command
    NF_DETECT_RB();
//       while(NFConDone==0);
     rNFCONT&=~(1<<9);
     rNFCONT&=~(1<<10); // Disable Illegal Access Interrupt
     if(rNFSTAT&0x8) return FAIL;

    NF_CMD(0x70);   // Read status command

      if (NF_RDDATA()&0x1) // Erase error
      {
        NF_nFCE_H();
    //Uart_Printf("[ERASE_ERROR:block#=%d]\n",block);
//    NFMarkBadBlock(block);
    return FAIL;
       }
       else
       {
        NF_nFCE_H();
       return OK;
       }
}

void NF_Reset(void)
{
    NF_nFCE_L();

    NF_CLEAR_RB();
    NF_CMD(0xFF);    //reset command
    NF_DETECT_RB();

    NF_nFCE_H();

}

void NF_Init(void)
{
    // for S3C2440

    rCLKCON |= 1 << 4; // Enable NAND flash clock (HCLK)

    rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)| /*(1<<3) | (1<<2) |*/ (0<<0);
    // TACLS        [14:12]    CLE&ALE duration = HCLK*TACLS.
    // TWRPH0        [10:8]    TWRPH0 duration = HCLK*(TWRPH0+1)
    // TWRPH1        [6:4]    TWRPH1 duration = HCLK*(TWRPH1+1)
    // AdvFlash(R)    [3]        Advanced NAND, 0:256/512, 1:1024/2048
    // PageSize(R)    [2]        NAND memory page size
    //                        when [3]==0, 0:256, 1:512 bytes/page.
    //                        when [3]==1, 0:1024, 1:2048 bytes/page.
    // AddrCycle(R)    [1]        NAND flash addr size
    //                        when [3]==0, 0:3-addr, 1:4-addr.
    //                        when [3]==1, 0:4-addr, 1:5-addr.
    // BusWidth(R/W) [0]    NAND bus width. 0:8-bit, 1:16-bit.

    rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(1<<1)|(1<<0);
    // Lock-tight    [13]    0:Disable lock, 1:Enable lock.
    // Soft Lock    [12]    0:Disable lock, 1:Enable lock.
    // EnablillegalAcINT[10]    Illegal access interupt control. 0:Disable, 1:Enable
    // EnbRnBINT    [9]        RnB interrupt. 0:Disable, 1:Enable
    // RnB_TrandMode[8]        RnB transition detection config. 0:Low to High, 1:High to Low
    // SpareECCLock    [6]        0:Unlock, 1:Lock
    // MainECCLock    [5]        0:Unlock, 1:Lock
    // InitECC(W)    [4]        1:Init ECC decoder/encoder.
    // Reg_nCE        [1]        0:nFCE=0, 1:nFCE=1.
    // NANDC Enable    [0]        operating mode. 0:Disable, 1:Enable.

    rNFSTAT = 0;

    NF_Reset();
}


 int NF_IsBadBlock(U32 block)
{
    unsigned int blockPage;
    U8 data;


        blockPage=(block<<5);    // For 2'nd cycle I/O[7:5]

    NF_nFCE_L();
    NF_CLEAR_RB();

    NF_CMD(0x50);         // Spare array read command
    NF_ADDR((512+5)&0xf);         // Read the mark of bad block in spare array(M addr=5), A4-A7:Don't care

    NF_ADDR(blockPage&0xff);     // The mark of bad block is in 0 page
    NF_ADDR((blockPage>>8)&0xff);     // For block number A[24:17]
    NF_ADDR((blockPage>>16)&0xff);  // For block number A[25]

    NF_DETECT_RB();     // Wait tR(max 12us)

        data=NF_RDDATA();

    NF_nFCE_H();

    if(data!=0xff)     return FAIL;
    else      return OK;

}

#if 0

void CopySplFromNand(unsigned char *pRam, int splSize)
{
    U8 *bufPt=(U8 *)pRam;
    int page, i;

    rCLKCON |= 1 << 4; // Enable NAND flash clock (HCLK)
    rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)| /*(1<<3) | (1<<2) |*/ (0<<0);
    rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(1<<1)|(1<<0);
    rNFSTAT = 0;

    NF_nFCE_L();
    NF_CLEAR_RB();
    NF_CMD(0xFF);    //reset command
    NF_DETECT_RB();
    NF_nFCE_H();

    for (page = 0; page < splSize; page++) {
        NF_RSTECC();    // Initialize ECC
        NF_MECC_UnLock();

        NF_nFCE_L();

        NF_CLEAR_RB();
        NF_CMD(0x00);    // Read command
        NF_ADDR(0);     // Column = 0
        NF_ADDR(0);
        NF_ADDR((page)&0xff);    // Block & Page num.
        NF_ADDR((page>>8)&0xff);    //
        NF_ADDR((page>>16)&0xff);

        NF_CMD(0x30);    // Read command
        NF_DETECT_RB();

        for (i = 0; i < FLASH_PAGESIZE; i++) {
            *bufPt++=NF_RDDATA8();    // Read one page
        }


        NF_MECC_Lock();

        NF_nFCE_H();
    }

}

#endif



int Do_InitStorage(void)
{
    return 0;
}

int Do_ReadKernel(unsigned char *pDst, unsigned long offset, unsigned long len)
{
    int totalPages = len / FLASH_PAGESIZE;
    int block = offset / FLASH_BLOCKSIZE;
    int page = (offset % FLASH_BLOCKSIZE) / FLASH_PAGESIZE;
    int i = 0;

    // Second, write all data into the flash
    while (i < totalPages) {
        //printk("i=%d, block=%d, page=%d\n", i, block, page);
        NF_ReadPage(block, page, pDst + i * FLASH_PAGESIZE);
        page++;
        i++;
        if (page == FLASH_PAGESOFBLOCK) {
            page = 0;
            block++;
        }
    }

    return 0;
}

int Do_WriteKernel(unsigned char *pSrc, unsigned long offset, unsigned long len)
{
    int totalPages = (len + FLASH_PAGESIZE - 1) / FLASH_PAGESIZE;
    int block = offset / FLASH_BLOCKSIZE;
    int page = (offset % FLASH_BLOCKSIZE) / FLASH_PAGESIZE;
    int i = 0;

//    printf("block=%d, page=%d, totalPages=%d, len=%d\n", block, page, totalPages, len);
    // First, erase the choosing block of flash memory
    NF_EraseBlock(block);

    // Second, write all data into the flash
    while (i < totalPages) {
        //printk("i=%d, block=%d, page=%d\n", i, block, page);
        NF_WritePage(block, page, pSrc + i * FLASH_PAGESIZE);
        page++;
        i++;
        if (page == FLASH_PAGESOFBLOCK) {
            if (i < totalPages) {
                page = 0;
                block++;
                NF_EraseBlock(block);
            }
        }
    }

//    printf("block=%d, page=%d, totalPages=%d, i=%d\n", block, page, totalPages, i);

    return 0;
}

int Do_ReadLoader(unsigned char * addr)
{
    return 0;
}

int Do_WriteLoader(unsigned char* addr, unsigned long len)
{
    return Do_WriteKernel(addr, 0, len);
}
